# Copyright (c) 2018 Presto Labs Pte. Ltd.
# Author: xguo

from web3 import Web3, HTTPProvider
import eth_account

import coin.exchange.base.kr_rest.private_client_base as privcb
import coin.exchange.uniswap_v2.kr_rest.native_private_client as nprivc
from coin.exchange.uniswap_v2.kr_rest.product import UniswapProduct
import coin.proto.coin_order_enums_pb2 as coin_order
from coin.proto.coin_query_pb2 import (
    AccountBalance,
    CurrencyBalance,
    ProductFillElement,
    ProductFillBundle,
    ProductOrderElement,
    ProductOrderBundle,
)
from coin.exchange.uniswap_base.constants import PRODUCTION_RPC_URL


def _get_order_side(side: str):
  if side == "sell":
    side_proto = coin_order.SELL_ORDER
  elif side == "buy":
    side_proto = coin_order.BUY_ORDER
  else:
    raise ValueError("Invalid order side %s" % side)
  return side_proto


class UniswapPrivateParser(object):
  @staticmethod
  def parse_balance(update_msg):
    if isinstance(update_msg, list):
      currency_balance_list = [
          CurrencyBalance(
              currency=update_item["currency"],
              currency_native=update_item["currency"],
              available=update_item["volume"],
              hold=update_item["hold"],
              total=update_item["hold"] + update_item["volume"],
          ) for update_item in update_msg
      ]
    else:
      currency_balance_list = update_msg["msg"].each_balance
    return AccountBalance(exchange="Uniswap",
                          market_type="Spot",
                          each_balance=currency_balance_list)


def construct_uniswap_instance(key):
  instance = Web3(provider=HTTPProvider(PRODUCTION_RPC_URL['ethereum']))
  account = eth_account.Account.from_key(key)
  return instance, account.address, account


class UniswapPrivateClient(privcb.PrivateClientBase):
  ProductType = UniswapProduct

  def __init__(self, private_key, og_info):
    privcb.PrivateClientBase.__init__(self)
    pool_symbol_list = [prod.symbol for prod in og_info.products]
    currency_symbol_list = [curr.native_currency for curr in og_info.currencies]
    uniswap_instance, wallet_address, acct = construct_uniswap_instance(private_key)

    self.nprivc = nprivc.UniswapV2NativePrivateClient(
        wallet_address=wallet_address,
        uniswap_instance=[uniswap_instance],
        pool_symbol_list=pool_symbol_list,
        currency_symbol_list=currency_symbol_list,
        acct=acct
    )

  def query_account_balance_impl(self):
    update = self.nprivc.get_balances()
    update.msg = UniswapPrivateParser.parse_balance(update.msg)
    return update

  def query_fills_impl(self, product):
    raise NotImplementedError()

  def query_list_orders_impl(self, product):
    raise NotImplementedError()

  def query_submit_impl(self, product, side, price, qty):
    return self.nprivc.place_order_and_fill_or_fail(product=product,
                                                    price=price,
                                                    quantity=qty,
                                                    order_side=side)

  def query_cancel_product_impl(self, product):
    raise NotImplementedError()
